#include <QPainter>
#include <QColor>
#include <QPoint>
#include <QFont>
#include <math.h>
#include <QDebug>
#include <QMessageBox>
#include <QClipboard>
#include <QApplication>
#include <QFileDialog>
#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    this->file = new QFile;
    this->line_list.append("");

    this->start_index = 0;
    this->start_column = 0;

    this->w = this->width();
    this->h = this->height();

    this->max_line = 1;
    while (this->max_line * 35 + 26 <= this->h) {
        ++this->max_line;
    }
    this->max_line -= 2;

    this->max_column = 0;
    while (this->max_column * 35 <= this->w) {
        ++this->max_column;
    }
    --this->max_column;

    this->is_cursor_show = false;
    this->is_ctrl_down = false;

    this->cursor_thread = new CursorThread;
    connect(this->cursor_thread, SIGNAL(send_cursor_status(bool)), this, SLOT(receive_cursor_status(bool)));
    connect(this, SIGNAL(send_break()), this->cursor_thread, SLOT(receive_break()));
    this->cursor_thread->start();

    this->cursor_point.setX(0);
    this->cursor_point.setY(0);

    this->start_point.setX(0);
    this->start_point.setY(0);
    this->end_point.setX(0);
    this->end_point.setY(0);

    this->setAttribute(Qt::WA_InputMethodEnabled, true);
}

MainWindow::~MainWindow()
{
    emit this->send_break();
    this->cursor_thread->wait(1000);
    this->cursor_thread->exit(0);
    this->file->close();
    delete this->file;
    delete this->cursor_thread;
    delete ui;
}

void MainWindow::paintEvent(QPaintEvent *) {
    QPainter painter(this);
    int _start_x = this->start_point.x();
    int _start_y = this->start_point.y();
    int _end_x = this->end_point.x();
    int _end_y = this->end_point.y();
    for (int x = 0; x < this->w; ++x) {
        for (int y = 0; y < this->h; ++y) {
            int _x_ = round(x / 35);
            int _y_ = round((y - 10) / 35) - 1;
            int list_len = this->line_list.length() - this->start_index;
            if (_y_ > list_len - 1) {
                _y_ = list_len - 1;
            }
            if (_y_ < 0) {
                _y_ = 0;
            }
            if ((_start_x == 0 || _start_y == 0) ||
                    (_end_x == 0 || _end_y == 0)) {
                painter.setPen(QColor(255, 255, 255));
            } else if (_x_ >= _start_x && _y_ >= _start_y &&
                    _x_ <= _end_x && _y_ <= _end_y) {
                painter.setPen(QColor(0, 0, 255));
            } else {
                painter.setPen(QColor(255, 255, 255));
            }
            painter.drawPoint(x, y);
        }
    }

    painter.setPen(QColor(0, 0, 0));
    painter.setFont(QFont("", 20));
    int line_start_index = 0;
    int line_index = this->start_index;
    int column_index = 0;
    int line_length = this->line_list.size();
    int max_line_number = this->max_line >= line_length ? line_length : this->max_line;
    for (; line_start_index < max_line_number; ++line_start_index) {
        if (this->start_index >= 0 && line_index <= line_length - 1) {
            QString line = this->line_list.at(line_index);
            ++ line_index;
            int line_len = line.length();
            int column_length = this->max_column >= line_len ? line_len : this->max_column;
            for (int _column_index = this->start_column; _column_index < column_length; ++_column_index) {
                if (column_index < line.length()) {
                    painter.drawText(column_index * 35, (line_start_index + 1) * 35 + 26, line.at(_column_index));
                    ++ column_index;
                }
            }
            column_index = 0;
        }
    }

    // draw cursor
    if (this->is_cursor_show == true) {
        painter.setPen(QColor(0, 0, 0));
        int _x = this->cursor_point.x() * 35;
        int _y = this->cursor_point.y() * 35;
        for (int y = _y; y < _y + 35; ++y) {
            painter.drawPoint(_x, y + 26);
        }
    } else {
        painter.setPen(QColor(255, 255, 255));
        int _x = this->cursor_point.x() * 35;
        int _y = this->cursor_point.y() * 35;
        for (int y = _y; y < _y + 35; ++y) {
            painter.drawPoint(_x, y + 26);
        }
    }

    // draw status bar
    painter.setPen(QColor(0, 0, 0));
    for (int x = 0; x < this->w; ++x) {
        for (int y = (this->max_line + 1) * 35; y < this->h; ++y) {
            painter.drawPoint(x, y);
        }
    }
    painter.setPen(QColor(255, 255, 255));
    painter.drawText(0, (this->max_line + 2) * 35,
        "当前：" + (QString::number(this->start_index + 1)) + "-" +
        QString::number(this->max_line) +
        "  总：" + QString::number(line_length));
}

void MainWindow::keyPressEvent(QKeyEvent *event) {
    int key = event->key();
    QString text = event->text();
    int _x = this->cursor_point.x();
    int _y = this->cursor_point.y() + this->start_index;
    QString line = this->line_list[_y];

    switch (key) {
    case Qt::Key_Left:
        if (_x > 0) {
            this->cursor_point.setX(_x - 1);
            this->update();
        }
        break;
    case Qt::Key_Right:
    {
        int line_len = line.length();
        if (_x < line_len) {
            this->cursor_point.setX(_x + 1);
            this->update();
        }
    }
        break;
    case Qt::Key_Up:
    {
        if (_y > 0) {
            int prev_len = this->line_list[_y - 1].length();
            if (_x >= prev_len) {
                this->cursor_point.setX(prev_len);
            }
            this->cursor_point.setY(_y - 1);
            this->update();
        }
    }
        break;
    case Qt::Key_Down:
    {
        int line_len = this->line_list.length();
        if (_y < line_len - 1) {
            int next_len = this->line_list[_y + 1].length();
            if (_x >= next_len) {
                this->cursor_point.setX(next_len);
            }
            this->cursor_point.setY(_y + 1);
            this->update();
        }
    }
        break;
    case Qt::Key_Return:
    {
        QString right_str = line.right(line.length() - _x);
        this->line_list.insert(_y + 1, right_str);
        this->line_list[_y] = line.left(_x);
        this->cursor_point.setX(0);
        this->cursor_point.setY(_y + 1);
        this->update();
    }
        break;
    case Qt::Key_Backspace:
    {
        if (_x > 0) {
            this->line_list[_y] = line.left(_x - 1) + line.right(line.length() - _x);
            this->cursor_point.setX(_x - 1);
            this->update();
        } else {
            if (_y > 0) {
                int src_len = this->line_list[_y - 1].length();
                this->line_list[_y - 1] += line;
                this->line_list.removeAt(_y);
                this->cursor_point.setX(src_len);
                this->cursor_point.setY(_y - 1);
                this->update();
            }
        }
    }
        break;
    case Qt::Key_Control:
    {
        this->is_ctrl_down = true;
    }
        break;
    case Qt::Key_C:
    {
        if (this->is_ctrl_down == true) {
            int select_x = this->start_point.x();
            int select_y = this->start_point.y();
            int select_end_x = this->end_point.x();
            QString select_line = this->line_list[select_y];
            QString sel = select_line.mid(select_x, select_end_x - select_x + 1);
            QClipboard *clip = QApplication::clipboard();
            clip->setText(sel);
        }
    }
        break;
    case Qt::Key_V:
    {
        QClipboard *clip = QApplication::clipboard();
        QString str = clip->text();
        this->line_list[_y] = line.left(_x) + str + line.right(line.length() - _x);
        this->cursor_point.setX(_x + str.length());
        this->update();
    }
        break;
    default:
        if (key >= 0 && key <= 127) {
            this->line_list[_y] = line.left(_x) + text + line.right(line.length() - _x);
            this->cursor_point.setX(_x + text.length());
            this->update();
        }
        break;
    }
}
void MainWindow::keyReleaseEvent(QKeyEvent *event) {
    int key = event->key();
    switch (key) {
    case Qt::Key_Control:
    {
        this->is_ctrl_down = false;
    }
        break;
    }
}

void MainWindow::mousePressEvent(QMouseEvent *event) {
    if (event->button() == Qt::LeftButton) {
        double x = event->x();
        double y = event->y();
        int _x = round(x / 35);
        int _y = round((y - 10) / 35) - 1;
        int list_len = this->line_list.length() - this->start_index;
        if (_y > list_len - 1) {
            _y = list_len - 1;
        }
        if (_y < 0) {
            _y = 0;
            this->line_list.append("");
        }
        int line_len = this->line_list[_y].length();
        if (_x > line_len) {
            _x = line_len;
        }
        this->cursor_point.setX(_x);
        if (_y >= this->max_line) {
            _y = this->max_line - 1;
        }
        this->cursor_point.setY(_y);

        this->start_point.setX(_x);
        this->start_point.setY(_y);

        this->update();
    }
}

void MainWindow::mouseMoveEvent(QMouseEvent *event) {
    int _x = this->start_point.x();
    int _y = this->start_point.y();
    if (_x > -1 || _y > -1) {
        double x = event->x();
        double y = event->y();
        int _x_ = round(x / 35);
        int _y_ = round((y - 10) / 35) - 1;
        int list_len = this->line_list.length() - this->start_index;
        if (_y_ > list_len - 1) {
            _y_ = list_len - 1;
        }
        if (_y_ < 0) {
            _y_ = 0;
        }
        int line_len = this->line_list.at(_y_).length();
        if (_x_ > line_len) {
            _x_ = line_len;
        }
        this->end_point.setX(_x_);
        this->end_point.setY(_y_);
        this->update();
    }
}

void MainWindow::inputMethodEvent(QInputMethodEvent *event) {
    QString text = event->commitString();
    if (text.length() > 0) {
        int x = this->cursor_point.x();
        int y = this->cursor_point.y();
        QString line = this->line_list[y];
        this->line_list[y] = line.left(x) + text + line.right(line.length() - x);
        this->cursor_point.setX(x + text.length());
        this->update();
    }
}

void MainWindow::wheelEvent(QWheelEvent *event) {
    QPoint p = event->angleDelta();
    int y = p.y();
    if (this->is_ctrl_down == true) {
        int mc = 0;
        this->max_column = this->start_column;
        while (mc * 35 <= this->w) {
            ++this->max_column;
            ++mc;
        }
        --this->max_column;
        if (y > 0) {
            if (this->start_column >= 3) {
                this->start_column -= 3;
                this->update();
            } else {
                this->start_column = 0;
                this->update();
            }
        } else if (y < 0) {
            this->start_column += 3;
            this->update();
        }
    } else {
        int _y = this->cursor_point.y();
        if (y > 0) {
            if (this->start_index > 3) {
                this->start_index -= 3;
                this->cursor_point.setY(_y + 3);
                this->update();
            } else {
                this->cursor_point.setY(this->start_index);
                this->start_index = 0;
                this->update();
            }
        } else if (y < 0) {
            this->start_index += 3;
            this->cursor_point.setY(_y - 3);
            this->update();
        }
    }
}

void MainWindow::receive_cursor_status(bool is_show) {
    this->is_cursor_show = is_show;
    this->update();
}


void MainWindow::on_action_triggered()
{
    this->line_list.clear();
    this->line_list.append("");
    this->file_name = "";
    this->cursor_point.setX(0);
    this->cursor_point.setY(0);
    this->update();
}

void MainWindow::on_action_2_triggered()
{
    this->file_name = QFileDialog::getOpenFileName(this, "请选择要打开的文件", ".",
        "All files (*);;Text files (*.txt);;C/C++ source files (*.c *.cpp *.cc)");
    this->file->setFileName(this->file_name);
    if (this->file->exists()) {
        if (this->file->open(QIODevice::ReadWrite | QIODevice::Text) == true) {
            this->line_list.clear();
            while (this->file->atEnd() == false) {
                this->line_list.append(this->file->readLine());
            }
            if (this->line_list.length() == 0) {
                this->line_list.append("");
            }
            this->update();
        } else {
            QMessageBox::critical(this, "错误", "文件无法打开");
        }
    } else {
        QMessageBox::critical(this, "错误", "文件不存在");
    }
}

void MainWindow::on_action_3_triggered()
{
    QString content = this->line_list.join("\r\n");
    if (this->file->write(content.toLatin1()) == -1) {
        QMessageBox::critical(this, "错误", "文件保存失败");
    } else {
        if (this->file->flush() == false) {
            QMessageBox::critical(this, "错误", "文件刷新失败");
        }
    }
}

